#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include "network.h"
#include "tools.h"

char buf[256] = {};
NetWork* data_nw;

void f_type(NetWork* nw)
{
	//文件类型
	sprintf(buf,"TYPE I\n");
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);
}

void f_pwd(NetWork* nw) 
{
	//路径
	sprintf(buf,"PWD\n");
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);

}

void f_stor(NetWork* nw,char* nam)
{
	//上传文件
	sprintf(buf,"STOR ");
	strcat(buf,nam);
	strcat(buf,"\n");
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);
}

bool f_retr(NetWork* nw,char* nam)
{
	//下载文件
	sprintf(buf,"RETR ");
	strcat(buf,nam);
	strcat(buf,"\n");
	printf("buf:%s",buf);
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);
	if(buf[0]=='5'&&buf[1]=='5'&&'0'==buf[2]) return false;
	return true;
}

void f_mdtm(NetWork* nw,char* nam)
{
	//文件修改时间
	sprintf(buf,"MDTM ");
	strcat(buf,nam);
	strcat(buf,"\n");
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);
}

void f_mdtm2(NetWork* nw,char* nam)
{
	//文件修改时间与路径
	f_mdtm(nw,nam);
	char time[1024] = {};
	for(int i=0;i<sizeof(buf);i++)
	{
		time[i] = buf[i];
	}

	char path[1024] = {};
	f_pwd(nw);
	for(int i=0;i<sizeof(buf);i++)
	{
		path[i] = buf[i];
	}	

	sprintf(buf,"MDTM ");
	strcat(buf,time);
	strcat(buf,path);
	strcat(buf,nam);
	strcat(buf,"\n");
	printf("%s",buf);
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);
}


void f_size(NetWork* nw,char* nam)
{
	//文件大小
	sprintf(buf,"SIZE ");
	strcat(buf,nam);
	strcat(buf,"\n");
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);
}

void f_pasv(NetWork* nw)
{
	//打开数据端口
	sprintf(buf,"PASV\n");
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);

	unsigned char ip1,ip2,ip3,ip4,port1,port2;
	sscanf(strchr(buf,'(')+1,"%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",&ip1,&ip2,&ip3,&ip4,&port1,&port2);

	sprintf(buf,"%hhu.%hhu.%hhu.%hhu",ip1,ip2,ip3,ip4);

	data_nw = open_network('c',SOCK_STREAM,buf,port1*256+port2);
	printf("connect success fd=%d",data_nw->fd);
	bzero(buf,sizeof(buf));
}

void f_cwd(NetWork* nw,char* pat)
{
	f_pasv(nw);
	//系统信息
	sprintf(buf,"CWD ");
	strcat(buf,pat);
	strcat(buf,"\n");
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));	
	puts(buf);
}

void f_syst(NetWork* nw)
{
	//系统信息
	sprintf(buf,"SYST\n");
	nsend(nw,buf,strlen(buf));

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);
}

void f_opts(NetWork* nw)
{
	//系统型号
	sprintf(buf,"OPTS UTF8 ON\n");
	nsend(nw,buf,strlen(buf));
	
	bzero(buf,sizeof(buf));	
	nrecv(nw,buf,sizeof(buf));
	puts(buf);
	
}


void f_list(NetWork* nw)
{
	//文件列表
	sprintf(buf,"LIST -al\n");
	nsend(nw,buf,strlen(buf));
	printf("buf:%s\n",buf);
	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);

	int ret = 0;
	bzero(buf,sizeof(buf));
	while(ret = nrecv(data_nw,buf,sizeof(buf)))
	{
		for(int i=0; i<ret; i++)
		{
			printf("%c",buf[i]);
		}
	}
	printf("\n");

	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);
	close_network(data_nw);
}


int main(int argc,char* argv[])
{
	if(argc != 2)
 	{
 		printf("Usage: %s IP Address\n",argv[0]);
  		return -1;
	 }
 
 	if(0 != strcmp(argv[1],"192.168.43.201"))
 	{
 		 printf("IP Address Error!\n");
 		 return -1;
 	}

	if(0 !=strcmp("./ftp",argv[0]))
	{
		printf("Invalid command!\n");
 		 return -1;
	}


	char user[20] = {};
	char pass[10] = {};

	printf("Connected to %s.\n",argv[1]);

	NetWork* nw = open_network('c',SOCK_STREAM,argv[1],21);//连接IP
	if(NULL == nw)
	{
		printf("open network return null!\n");
		return -1;
	}
	
	nrecv(nw,buf,sizeof(buf));
	puts(buf);

	printf("Name (%s:zhizhen):",argv[1]);
	scanf("%s",user);
	clear_stdin();

	sprintf(buf,"USER %s\n",user);
	nsend(nw,buf,strlen(buf));
	
	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);

	printf("Password:");
	get_passwd(pass,true,10);
	

	sprintf(buf,"PASS %s\n",pass);
	nsend(nw,buf,strlen(buf));
	
	bzero(buf,sizeof(buf));
	nrecv(nw,buf,sizeof(buf));
	puts(buf);

	if(0 != strncmp("230",buf,3))
	{
		printf("Login failed.\n");
		return 0;
	}
	//打开数据端口
	f_pasv(nw);	
		
	//显示文件目录
	f_list(nw);
	
	char cmd[10] = {};
	char pat[20] = {};
	while(true)
	{
		printf("ftp>");
		scanf("%s",cmd);
		if(0 == strcmp("bye",cmd))
		{
			printf("221 Goodbye.\n");
			break;
		}
		if(0 == strcmp("cd",cmd))
		{
			scanf("%s",pat);
			f_cwd(nw,pat);
			f_pwd(nw);
			f_pasv(nw);
			f_list(nw);
		}
		if(0 == strcmp("ls",cmd))
		{
			f_pasv(nw);
			f_list(nw);
		}
		if(0 == strcmp("get",cmd))//下载
		{
			scanf("%s",pat);
			f_type(nw);
			f_size(nw,pat);
			f_mdtm(nw,pat);
			f_pasv(nw);
			if(!f_retr(nw,pat)) continue;
			int fd = open(pat,O_WRONLY|O_CREAT|O_TRUNC,0644);
			if(0 > fd)
			{
				perror("open");
				return -1;
			}
			int ret;
			while(ret = nrecv(data_nw,buf,sizeof(buf)))
			{
				write(fd,buf,ret);
			}
			close(fd);
			close_network(data_nw);
			
			bzero(buf,sizeof(buf));
			nrecv(nw,buf,sizeof(buf));
			puts(buf);
		}
		if(0 == strcmp("put",cmd))//上传
		{
			scanf("%s",pat);

			DIR* dp = opendir(pat);
			if(NULL != dp)
			{	
				printf("open fail.\n");
				continue;
			}
			else
			{			
				closedir(dp);

				f_size(nw,pat);
				f_pasv(nw);
				f_stor(nw,pat);
			
				//获取文件大小
				struct stat ato = {};
				if(stat(pat,&ato))
				{
					perror("stat");
					return -1;
				}
				int size = (int)ato.st_size;
			

				//传文件
				int fd = open(pat,O_RDONLY,0644);
				if(0 > fd)
				{
					perror("open");
					return -1;
				}
				int ret;
				while(size>=256)
				{
					read(fd,buf,255);
						
					nsend(data_nw,buf,strlen(buf));
					size = size-255;
				}	
				for(int i=0;i<256;i++) buf[i] = '\0';

				if(size < 256)
				{
					read(fd,buf,size);		
			
					nsend(data_nw,buf,strlen(buf));
				}
				close(fd);
				printf("ok\n");	
			}	
		}
	}
}
